# -*- coding: utf-8 -*- #
"""
Time                2023/3/17 19:25
Author:             mingfeng (SunnyQjm)
Email               mfeng@linux.alibaba.com
File                service_registry.py
Description:
"""
import importlib
from abc import ABCMeta, abstractmethod
from clogger import logger
from .service_instance import ServiceInstance
from .exceptions import CmgProtoAlreadyExistsException, \
    CmgProtoNotExistsException
from .url import CmgUrl


class ServiceRegistry(metaclass=ABCMeta):
    """Common Microservice Governance ServiceRegistry interface definition

    This interface defines the generic behavior of the CMG ServiceRegistry.

    """
    proto_dict = {}

    @abstractmethod
    def register(self, service_instance: ServiceInstance):
        """Register one service to register center"""
        pass

    @abstractmethod
    def unregister(self, service_id: str):
        """Unregister specific service from register center"""
        pass

    @staticmethod
    def protocol_register(proto, sub_class):
        """Register one new protocol => indicate one execution module

        Register a new protocol => This function is called by the executing
        module to register its own implementation of Producer for the executing
        module to take effect.
        (Usually when the execution module is implemented according to the
        specification, there is no need for the developer to call this method
        manually, the abstraction layer will dynamically import)

        Args:
            proto(str): Protocol identification
            sub_class: Implementation class of Producer

        Returns:

        Examples:
            >>> ServiceRegistry.protocol_register('redis', RedisServiceRegistry)

        """
        if proto in ServiceRegistry.proto_dict:
            err = CmgProtoAlreadyExistsException(
                f"Proto '{proto}' already exists in Cmg-base-ServiceRegistry."
            )
            logger.error(err)
            raise err
        ServiceRegistry.proto_dict[proto] = sub_class
        logger.info(
            f"Cmg-base-ServiceRegistry register proto '{proto}' success"
        )


def dispatch_service_registry(url: str, **kwargs) -> ServiceRegistry:
    """Construct one ServiceRegistry instance according the url

    Construct a ServiceRegistry instance of the corresponding type based on
    the URL passed in.

    Args:
        url(str): CmgUrl

    Returns:
        ServiceRegistry: one ServiceRegistry instance

    Examples:
        >>> service_registry = dispatch_service_registry(
            ..."redis://localhost:6379?password=123456")
    """
    cmg_url = CmgUrl.parse(url)
    if cmg_url.proto not in ServiceRegistry.proto_dict:
        # Check if dynamic import is possible
        target_module = f"cmg_{cmg_url.proto}.{cmg_url.proto}_service_registry"
        try:
            module = importlib.import_module(target_module)
            ServiceRegistry.protocol_register(
                cmg_url.proto,
                getattr(module, f'{cmg_url.proto.capitalize()}ServiceRegistry')
            )
        except ModuleNotFoundError as exc:
            logger.error(
                f"Try to auto import module {target_module} failed."
            )
            raise CmgProtoNotExistsException(
                f"Proto '{cmg_url.proto}' not exists in Cmg-base"
                "-ServiceRegistry."
            ) from exc
    service_registry_instance = ServiceRegistry.proto_dict[cmg_url.proto](
        cmg_url, **kwargs)
    logger.info(
        f"Cmg-base-ServiceRegistry dispatch one ServiceRegistry instance "
        f"success. proto={cmg_url.proto}, url={url}"
    )
    return service_registry_instance
